home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C++ / Libraries / grayimage / read_tiff.cc < prev    next >
Encoding:
Text File  |  1994-06-30  |  10.3 KB  |  392 lines  |  [TEXT/R*ch]

  1. // This may look like C code, but it is really -*- C++ -*-
  2. /*
  3.  ************************************************************************
  4.  *
  5.  *               Grayscale Images
  6.  *
  7.  *         Read an image from a file in the TIFF format
  8.  *
  9.  * The present TIFF reader reads a Class G TIFF file (for gray-scale
  10.  * images), See Appendix G of the TIFF specification. 
  11.  *
  12.  * The program really needs the following tags
  13.  *    ImageWidth
  14.  *    ImageLength
  15.  *    RowsPerStrip
  16.  *    StripOffsets
  17.  * and can't work without them. Yet, if RowsPerStrip is left out, we
  18.  * assume there is only one strip in the image
  19.  *
  20.  * In addition, class G TIFF files are required to have the following
  21.  * tags with the following values
  22.  *    SamplesPerPixel = 1
  23.  *    BitsPerSample   = 4 or 8
  24.  *    Compression    = 1 or 5(LZW)
  25.  *    PhotometricInterpretation = 0 or 1
  26.  * So, if these tags are specified, the program makes sure that their values
  27.  * are those that are supposed to be for class G TIFFs.
  28.  *
  29.  * At present, no compression is supported.
  30.  *
  31.  * $Id: read_tiff.cc,v 1.1 1994/01/13 14:45:30 oleg Exp oleg $
  32.  *
  33.  ************************************************************************
  34.  */
  35.  
  36. #include "image.h"
  37. #include "endian_io.h"
  38. #include "tiff.h"
  39.  
  40.  
  41.                 // Read a TIFF header from the file
  42.                 // and check it
  43. TIFFHeader::TIFFHeader(EndianIO& file)
  44. {
  45.   magic = file.read_short("Reading a TIFF file magic byte");
  46.   if( magic == TIFF_BIGENDIAN )
  47.     file.set_bigendian();
  48.   else if( magic == TIFF_LITTLEENDIAN )
  49.     file.set_littlendian();
  50.   else
  51.     _error("Unknown magic word %x",magic);
  52.   version = file.read_short("Reading TIFF file version");
  53.   diroffset = file.read_long("Reading the TIFF dir offset");
  54. }
  55.  
  56. /*
  57.  *------------------------------------------------------------------------
  58.  *            TIFF Directory
  59.  */
  60.  
  61. class TIFFDirectory;
  62.  
  63. class TIFFDirReadItem : public TIFFDirEntry
  64. {
  65.   friend class TIFFDirectory;
  66.                 // Note, this is a private constructor
  67.                 // Be sure to call the load(file) function
  68.                 // to finish the initialization
  69.   TIFFDirReadItem(void) { tag = 0; }
  70.   void load(EndianIO& file);
  71.  
  72. public:
  73.   ~TIFFDirReadItem(void) {}
  74.   void print(void) const;
  75. };
  76.  
  77.                 // Load up the item from the file: finish
  78.                 // the construction of the object
  79. void TIFFDirReadItem::load(EndianIO& file)
  80. {
  81.   tag   = file.read_short("Reading dir entry");
  82.   type  = file.read_short("Reading dir entry");
  83.   count = file.read_long("Reading dir entry");
  84.   val_offset = file.read_long("Reading dir entry");
  85. }
  86.  
  87.                 // Print the contents of an item w/o frills
  88. void TIFFDirReadItem::print(void) const
  89. {
  90.   message("tag %d, %d ",tag,count);
  91.   switch(type)
  92.   {
  93.     case BYTE:
  94.          message("bytes");
  95.      break;
  96.  
  97.     case ASCII:
  98.          message("characters");
  99.      break;
  100.  
  101.     case SHORT:
  102.          message("short ints");
  103.      break;
  104.  
  105.     case LONG:
  106.          message("long ints");
  107.      break;
  108.  
  109.     case RATIONAL:
  110.          message("rational numbers");
  111.      break;
  112.  
  113.     default:
  114.      _error("Invalid type %d",type);
  115.   }
  116.   message(" value %d (0x%x)\n",val_offset,val_offset);
  117. }
  118.  
  119.  
  120. class TIFFDirectory
  121. {
  122.   int no_entries;
  123.   TIFFDirReadItem * entries;
  124.  
  125. public:
  126.   TIFFDirectory(EndianIO& file);
  127.   ~TIFFDirectory(void);
  128.   void print(void) const;
  129.                 // Look up a scalar value in the directory
  130.   unsigned long int look_up(const unsigned short tag,
  131.                 const unsigned long default_value) const;
  132.                 // Look up a string info and read the string
  133.   char * read_str(const unsigned short tag, EndianIO& file) const;
  134.  
  135.                 // Look up an item in the directory by its
  136.                 // tag. Return 0 if not found
  137.   TIFFDirReadItem * look_up(const unsigned short tag) const;
  138. };
  139.  
  140.                 // Load up the directory from the file
  141. TIFFDirectory::TIFFDirectory(EndianIO& file)
  142. {
  143.   TIFFHeader header(file);
  144.   assert( file.seekp(header.directory_offset()).good() );
  145.   no_entries = file.read_short("no of entries");
  146.   entries = new TIFFDirReadItem[no_entries];
  147.  
  148.   register int i;
  149.   for(i=0; i<no_entries; i++)
  150.     entries[i].load(file);
  151.  
  152.   const int offset_to_next_dir = file.read_long("reading dir");
  153.   if( offset_to_next_dir != 0 )
  154.     message("The TIFF file contains several images, only the first one "
  155.         "is going to be considered");
  156. }
  157.  
  158.                 // Destroy all entries in the directory
  159. TIFFDirectory::~TIFFDirectory(void)
  160. {
  161.   assert( entries != 0 );
  162.   delete entries;
  163. }
  164.  
  165.                 // Print the contents of the directory
  166. void TIFFDirectory::print(void) const
  167. {
  168.   register int i;
  169.   message("\nThere are %d entries in the TIFF file directory\nThey are\n",
  170.       no_entries);
  171.   for(i=0;i<no_entries; i++)
  172.     entries[i].print();
  173. }
  174.  
  175.                 // Look up an item in the directory by its
  176.                 // tag. Return 0 if not found
  177.                 // Note, we take advantage of the fact
  178.                 // that all items in the directory are
  179.                 // in the ascending order of their tags
  180. TIFFDirReadItem * TIFFDirectory::look_up(const unsigned short tag) const
  181. {
  182.   int lo = 0;                // Using a binary search
  183.   int hi = no_entries;
  184.   while( hi > lo )
  185.   {
  186.     int middle = (hi+lo)/2;
  187.     if( entries[middle].tag == tag )
  188.       return &entries[middle];
  189.     else if( entries[middle].tag > tag )
  190.       hi = middle;
  191.     else
  192.       lo = middle+1;
  193.   }
  194.   return 0;                // Means not found
  195. }
  196.  
  197.  
  198.                 // Look up a scalar value in the directory
  199. unsigned long int TIFFDirectory::look_up(const unsigned short tag,
  200.                 const unsigned long default_value) const
  201. {
  202.   TIFFDirEntry * entryp = look_up(tag);
  203.   if( entryp == 0 )
  204.     return default_value;
  205.  
  206.   assert( entryp->tag == tag);
  207.   assert( entryp->count == 1 );        // Check that the value is scalar
  208.                     // Note, if the value is shorter
  209.                     // than long, it's LEFT justified
  210.   switch( entryp->type )
  211.   {
  212.     case TIFFDirEntry::BYTE:
  213.     case TIFFDirEntry::ASCII:
  214.          return (entryp->val_offset) >> 24;
  215.  
  216.     case TIFFDirEntry::SHORT:
  217.          return (entryp->val_offset) >> 16;
  218.  
  219.     case TIFFDirEntry::LONG:
  220.     case TIFFDirEntry::RATIONAL:
  221.          return (entryp->val_offset);
  222.  
  223.     default:
  224.      _error("Invalid type %d",entryp->type);
  225.   }
  226. }
  227.  
  228.                 // Look up a string value in the directory
  229.                 // and return a dynamically allocated str
  230.                 // Return 0 if not found
  231. char * TIFFDirectory::read_str(const unsigned short tag, EndianIO& file) const
  232. {
  233.   TIFFDirEntry * entryp = look_up(tag);
  234.   if( entryp == 0 )
  235.     return 0;
  236.   assert( entryp->tag == tag);
  237.   assert( entryp->type == TIFFDirEntry::ASCII );
  238.   
  239.   int size = entryp->count;
  240.   if( size <= 0 )
  241.     return 0;
  242.  
  243.   char * str = (char *)malloc(size);
  244.  
  245.   assert( file.seekp(entryp->val_offset).good() );
  246.  
  247.   file.read(str,size);
  248.   if( str[size-1] != '\0' )        // Make sure the str is terminated
  249.     str[size-1] = '\0';            // properly     
  250.  
  251.   return str;
  252. }
  253.  
  254.  
  255. class TIFFReadStrips
  256. {
  257.   int no_strips;
  258.   int rows_per_strip;
  259.   unsigned long int * strip_offsets;
  260. public:
  261.   TIFFReadStrips(const TIFFDirectory& directory,const int _rows_per_strip,
  262.          EndianIO& file);
  263.   TIFFReadStrips();
  264.   ~TIFFReadStrips(void);
  265.   void read(IMAGE& image, EndianIO& file);
  266. };
  267.  
  268. TIFFReadStrips::TIFFReadStrips(const TIFFDirectory& directory,
  269.                    const int _rows_per_strip,
  270.                    EndianIO& file)
  271.     : rows_per_strip(_rows_per_strip)
  272. {
  273.   TIFFDirEntry * entryp = directory.look_up(TIFFTAG_STRIPOFFSETS);
  274.   assure( entryp != 0, "STRIPOFFSETS tag must be present in the TIFF file");
  275.  
  276.   no_strips = entryp->count;
  277.   assert( no_strips > 0 );
  278.   strip_offsets = new long[no_strips];
  279.  
  280.   if( no_strips == 1 )            // The value is coded within the
  281.   {                    // dir entry itself
  282.     if( entryp->type == TIFFDirEntry::SHORT )
  283.       strip_offsets[0] = (entryp->val_offset) >> 16;
  284.     else if( entryp->type == TIFFDirEntry::LONG )
  285.       strip_offsets[0] = entryp->val_offset;
  286.     else
  287.       _error("StripOffsets must contain either LONG or SHORT values");
  288.     return;
  289.   }
  290.  
  291.                     // Otherwise we got to read them,
  292.                     // I mean, the strip offsets
  293.   assert( file.seekp(entryp->val_offset).good() );
  294.   
  295.   register int i;
  296.   for(i=0; i<no_strips; i++)
  297.     if( entryp->type == TIFFDirEntry::SHORT )
  298.       strip_offsets[i] = file.read_short("Reading StripOffsets");
  299.     else if( entryp->type == TIFFDirEntry::LONG )
  300.       strip_offsets[i] = file.read_long("Reading StripOffsets");
  301.     else
  302.       _error("StripOffsets must contain either LONG or SHORT values");
  303. }
  304.  
  305. TIFFReadStrips::~TIFFReadStrips(void)
  306. {
  307.   assert( strip_offsets != 0 );
  308.   delete strip_offsets;
  309. }
  310.  
  311.                 // Read in the strips into the image
  312. void TIFFReadStrips::read(IMAGE& image, EndianIO& file)
  313. {
  314.   register int strip;
  315.   for(strip=0; strip<no_strips; strip++)
  316.   {
  317.     assert( file.seekp(strip_offsets[strip]).good() );
  318.  
  319.     register int i,j;            // Reading a strip
  320.     for(i=strip*rows_per_strip; 
  321.     i<(strip+1)*rows_per_strip && i < image.q_nrows(); i++)
  322.       for(j=0; j<image.q_ncols(); j++)
  323.     image(i,j) = file.read_byte("reading a strip");
  324.   }
  325. }
  326.  
  327. /*
  328.  *========================================================================
  329.  *             Root module - actual IMAGE constructor
  330.  */
  331.  
  332. void IMAGE::read_tiff(EndianIO& file, const char verbose)
  333. {
  334.   message("Reading the TIFF file\n");
  335.  
  336.   TIFFDirectory directory(file);
  337.   
  338.   if( verbose )
  339.     directory.print();
  340.  
  341.   int no_cols = directory.look_up(TIFFTAG_IMAGEWIDTH,0);
  342.   if( no_cols == 0 )
  343.     _error("ImageWidth tag missing or improperly specified as 0");
  344.   
  345.   int no_rows = directory.look_up(TIFFTAG_IMAGELENGTH,0);
  346.   if( no_rows == 0 )
  347.     _error("ImageLength tag missing or improperly specified as 0");
  348.  
  349.                     // Checking if we can handle this
  350.   assert( directory.look_up(TIFFTAG_SAMPLESPERPIXEL,1) == 1 );
  351.   assert( directory.look_up(TIFFTAG_BITSPERSAMPLE,1) == 8 );
  352.   assert( directory.look_up(TIFFTAG_COMPRESSION,COMPRESSION_NONE) == 
  353.      COMPRESSION_NONE );
  354.  
  355.   int photometry = directory.look_up(TIFFTAG_PHOTOMETRIC,
  356.                      PHOTOMETRIC_MINISBLACK);
  357.   assert( photometry == PHOTOMETRIC_MINISBLACK ||
  358.       photometry == PHOTOMETRIC_MINISWHITE );
  359.  
  360.  
  361.   int rows_per_strip = directory.look_up(TIFFTAG_ROWSPERSTRIP,no_rows);
  362.   TIFFReadStrips strips(directory,rows_per_strip,file);
  363.  
  364.   allocate(no_rows,no_cols,8);
  365.  
  366.   {
  367.     char * descr = directory.read_str(TIFFTAG_IMAGEDESCRIPTION,file);
  368.     char * doc_name = directory.read_str(TIFFTAG_DOCUMENTNAME,file);
  369.     if( descr != 0 || doc_name != 0 )
  370.       if( doc_name == 0 )
  371.     name = descr;
  372.       else if( descr == 0 )
  373.     name = doc_name;
  374.       else
  375.     name = strcat(
  376.          strcat(
  377.            strcpy((char *)malloc(strlen(descr)+strlen(doc_name)+2),
  378.               descr),"/"),doc_name),
  379.         free(descr), free(doc_name);
  380.   }
  381.  
  382.  
  383.   strips.read(*this,file);
  384.  
  385.   if( photometry == PHOTOMETRIC_MINISWHITE )
  386.     invert();
  387.  
  388.   cout << "\n" << no_cols << "x" << no_rows << "x" << q_depth() << " image '"
  389.          << name << "' has been read\n";
  390. }
  391.  
  392.